﻿using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using log4net;

namespace Sendmail.Server
{
    public class SmtpServer : MarshalByRefObject
    {
        #region Properties

        #region Listener Properties
        public TcpListener Listener { private set; get; }
        #endregion

        #region CurrentConnectionId Properties
        private int _currentConnectionId;
        public int CurrentConnectionId
        {
            private set { _currentConnectionId = value; }
            get { return _currentConnectionId++; }
        }
        #endregion

        #region CurrentMailId Properties
        private int _currentMailId;
        public int CurrentMailId
        {
            private set { _currentMailId = value; }
            get { return _currentMailId++; }
        }
        #endregion

        #region ServerIsWorking Properties
        private bool _serverIsWorking;
        public bool ServerIsWorking
        {
            set
            {
                ServerListenerIsWorking = value;
                _serverIsWorking = value;
                LogInfo(value ? "Server is working." : "Server is down.");
            }
            get { return _serverIsWorking; }
        }
        #endregion

        #region ServerListenerIsWorking Properties
        private bool _serverListenerIsWorking;
        public bool ServerListenerIsWorking
        {
            set
            {
                _serverListenerIsWorking = value;
                if (value)
                {
                    Listener.Start();
                }
                else
                {
                    Listener.Stop();
                }
                LogInfo(value ? "Server's listener is working." : "Server's listener is close.");
            }
            get { return _serverListenerIsWorking; }
        }
        #endregion

        #region BadRequestMax Properties
        public int BadRequestMax { get; set; }
        #endregion

        #endregion

        #region Variables
        private readonly SmtpServerWorker _worker;
        private readonly Thread _thread;
        private string[] _mailPaths;
        #endregion


        #region Constructor
        public SmtpServer(IPAddress ip, int port)
        {
            LoadAccounts();
            _mailPaths = GetPathsToAllMails();

            BadRequestMax = 4;
            CurrentConnectionId = 0;
            CurrentMailId = _mailPaths != null ? _mailPaths.Length : 0;
            Listener = new TcpListener(ip, port);

            ServerIsWorking = true;

            _worker = new SmtpServerWorker(this);
            _thread = new Thread(_worker.Run);
            _thread.Start();

            //TODO remove this
            AddAccount("receiver");
            //RemoveAccount("receiver");
            //Console.WriteLine("Accounts: " + Settings.Default.Accounts);

            Log.Debug("SmtpServer loaded successfully.");
        }
        #endregion

        #region Logging
        private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        public void LogInfo(String text)
        {
            var d = DateTime.Now;
            Log.Info(string.Format("{0:0000}/{1:00}/{2:00} {3:00}:{4:00}:{5:00}.{6:000} - {7}", d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second, d.Millisecond, text));
        }
        #endregion

        #region Accounts Management
        public ObservableCollection<string> _accounts = new ObservableCollection<string>();

        public void LoadAccounts()
        {
            var accounts = Settings.Default.Accounts.Split(';');
            for (int i = 0; i < accounts.Length-1; i++)
            {
                var account = accounts[i];
                _accounts.Add(account);
                CreateDirectory(account);
                //Console.WriteLine("a " + account);
            }
        }

        public void AddAccount(string account)
        {
            if(!_accounts.Contains(account)){
                _accounts.Add(account);
                CreateDirectory(account);
                SaveAccounts();
            }
        }

        public void RemoveAccount(string account)
        {
            _accounts.Remove(account);
            SaveAccounts();
        }

        public void SaveAccounts()
        {
            var result = _accounts.Aggregate("", (current, account) => current + (account + ';'));
            Settings.Default.Accounts = result;
            Settings.Default.Save();

        }

        public static void CreateDirectory(string account)
        {
            var path = Environment.CurrentDirectory.ToString(CultureInfo.InvariantCulture) + "\\mailbox";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            path = Environment.CurrentDirectory.ToString(CultureInfo.InvariantCulture) + "\\mailbox\\" + account;
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }
        #endregion

        #region Email management
        public string[] GetPathsToAllMails()
        {
            var path = Environment.CurrentDirectory.ToString(CultureInfo.InvariantCulture) + "\\mailbox\\";
            if (Directory.Exists(path))
            {
                return Directory.GetFiles(path, "*.eml", SearchOption.AllDirectories);
            }
            return null;
        }

        public string[] GetPathsToAllMailsForAccount(string account)
        {
            var path = Environment.CurrentDirectory.ToString(CultureInfo.InvariantCulture) + "\\mailbox\\" + account;
            if (Directory.Exists(path))
            {
                return Directory.GetFiles(path, "*.eml", SearchOption.AllDirectories);
            }
            return null;
        }
        #endregion
    }
}
